<?php
namespace App\Http\Utils\OAuth1;

/**
 * @ignore
 */
class OAuthServer {
	protected $timestamp_threshold = 300; // in seconds, five minutes
	protected $version = 1.0;             // hi blaine
	protected $signature_methods = array();

	protected $data_store;

	function __construct($data_store) {
		$this->data_store = $data_store;
	}

	public function add_signature_method($signature_method) {
		$this->signature_methods[$signature_method->get_name()] =
		$signature_method;
	}

	// high level functions

	/**
	 * process a request_token request
	 * returns the request token on success
	 */
	public function fetch_request_token(&$request) {
		$this->get_version($request);

		$consumer = $this->get_consumer($request);

		// no token required for the initial token request
		$token = NULL;

		$this->check_signature($request, $consumer, $token);

		$new_token = $this->data_store->new_request_token($consumer);

		return $new_token;
	}

	/**
	 * process an access_token request
	 * returns the access token on success
	 */
	public function fetch_access_token(&$request) {
		$this->get_version($request);

		$consumer = $this->get_consumer($request);

		// requires authorized request token
		$token = $this->get_token($request, $consumer, "request");


		$this->check_signature($request, $consumer, $token);

		$new_token = $this->data_store->new_access_token($token, $consumer);

		return $new_token;
	}

	/**
	 * verify an api call, checks all the parameters
	 */
	public function verify_request(&$request) {
		$this->get_version($request);
		$consumer = $this->get_consumer($request);
		$token = $this->get_token($request, $consumer, "access");
		$this->check_signature($request, $consumer, $token);
		return array($consumer, $token);
	}

	// Internals from here
	/**
	 * version 1
	 */
	private function get_version(&$request) {
		$version = $request->get_parameter("oauth_version");
		if (!$version) {
			$version = 1.0;
		}
		if ($version && $version != $this->version) {
			throw new OAuthNewException("OAuth version '$version' not supported");
		}
		return $version;
	}

	/**
	 * figure out the signature with some defaults
	 */
	private function get_signature_method(&$request) {
		$signature_method =
		@$request->get_parameter("oauth_signature_method");
		if (!$signature_method) {
			$signature_method = "PLAINTEXT";
		}
		if (!in_array($signature_method,
				array_keys($this->signature_methods))) {
					throw new OAuthNewException(
							"Signature method '$signature_method' not supported " .
							"try one of the following: " .
							implode(", ", array_keys($this->signature_methods))
							);
				}
				return $this->signature_methods[$signature_method];
	}

	/**
	 * try to find the consumer for the provided request's consumer key
	 */
	private function get_consumer(&$request) {
		$consumer_key = @$request->get_parameter("oauth_consumer_key");
		if (!$consumer_key) {
			throw new OAuthNewException("Invalid consumer key");
		}

		$consumer = $this->data_store->lookup_consumer($consumer_key);
		if (!$consumer) {
			throw new OAuthNewException("Invalid consumer");
		}

		return $consumer;
	}

	/**
	 * try to find the token for the provided request's token key
	 */
	private function get_token(&$request, $consumer, $token_type="access") {
		$token_field = @$request->get_parameter('oauth_token');
		$token = $this->data_store->lookup_token(
				$consumer, $token_type, $token_field
				);
		if (!$token) {
			throw new OAuthNewException("Invalid $token_type token: $token_field");
		}
		return $token;
	}

	/**
	 * all-in-one function to check the signature on a request
	 * should guess the signature method appropriately
	 */
	private function check_signature(&$request, $consumer, $token) {
		// this should probably be in a different method
		$timestamp = @$request->get_parameter('oauth_timestamp');
		$nonce = @$request->get_parameter('oauth_nonce');

		$this->check_timestamp($timestamp);
		$this->check_nonce($consumer, $token, $nonce, $timestamp);

		$signature_method = $this->get_signature_method($request);

		$signature = $request->get_parameter('oauth_signature');
		$valid_sig = $signature_method->check_signature(
				$request,
				$consumer,
				$token,
				$signature
				);

		if (!$valid_sig) {
			throw new OAuthNewException("Invalid signature");
		}
	}

	/**
	 * check that the timestamp is new enough
	 */
	private function check_timestamp($timestamp) {
		// verify that timestamp is recentish
		$now = time();
		if ($now - $timestamp > $this->timestamp_threshold) {
			throw new OAuthNewException(
					"Expired timestamp, yours $timestamp, ours $now"
					);
		}
	}

	/**
	 * check that the nonce is not repeated
	 */
	private function check_nonce($consumer, $token, $nonce, $timestamp) {
		// verify that the nonce is uniqueish
		$found = $this->data_store->lookup_nonce(
				$consumer,
				$token,
				$nonce,
				$timestamp
				);
		if ($found) {
			throw new OAuthNewException("Nonce already used: $nonce");
		}
	}

}